﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK.Graphics.OpenGL;
namespace Common {
    public class DdsReader {
        const int FOURCC_DXT1 = 0x31545844;// Equivalent to "DXT1" in ASCII
        const int FOURCC_DXT3 = 0x33545844; // Equivalent to "DXT3" in ASCII
        const int FOURCC_DXT5 = 0x35545844; // Equivalent to "DXT5" in ASCII
        public static int LoadDds(string imagePath) {
            var stream = File.Open(imagePath, FileMode.Open);
            using (stream) {
                return loadDdsFromStream(stream);
            }
        }

        private static int loadDdsFromStream(FileStream stream) {
            byte[] header = new byte[124];

            byte[] fileCodeData = new byte[4];
            stream.Read(fileCodeData, 0, fileCodeData.Length);
            if (Encoding.ASCII.GetString(fileCodeData) != "DDS ") {
                throw new Exception("Not a valid DDS file");
            }
            stream.Read(header, 0, header.Length);

            int height = GLUtility.getIntFromPos(header, 8);
            int width = GLUtility.getIntFromPos(header, 12);
            int linearSize = GLUtility.getIntFromPos(header, 16);
            int mipMapCount = GLUtility.getIntFromPos(header, 24);
            int fourCC = GLUtility.getIntFromPos(header, 80);

            int bufferSize = mipMapCount > 1 ? linearSize * 2 : linearSize;
            byte[] buffer = new byte[bufferSize];
            stream.Read(buffer, 0, buffer.Length);

            int components = (fourCC == FOURCC_DXT1) ? 3 : 4;
            InternalFormat format;
            format = GetCompressFormat(fourCC);

            int textureId = GL.GenTexture();
            GL.BindTexture(TextureTarget.Texture2D, textureId);
            GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);

            int blockSize = (format == InternalFormat.CompressedRgbaS3tcDxt1Ext) ? 8 : 16;
            int offset = 0;
            for (int level = 0; (level < mipMapCount) && (width > 0) && (height > 0); ++level) {
                int size = ((width + 3) / 4) * ((height + 3) / 4) * blockSize;
                //GL.CompressedTexImage2D<byte>(TextureTarget.Texture2D, level, format, width, height, 0, size, buffer);
                CompressTexture(TextureTarget.Texture2D, level, format, width, height, 0, size, buffer, offset);
                offset += size;
                width /= 2;
                height /= 2;
                if (width < 1) width = 1;
                if (height < 1) height = 1;
            }
            return textureId;
        }

        unsafe private static void CompressTexture(TextureTarget textureTarget, int level, InternalFormat format, int width, int height, int v, int size, byte[] buffer, int offset) {
            fixed (byte* pBuffer = buffer) {
                byte* currentPointer = pBuffer + offset;
                IntPtr ptr = (IntPtr)currentPointer;
                GL.CompressedTexImage2D(textureTarget, level, format, width, height, v, size, ptr);
            }
        }

        private static InternalFormat GetCompressFormat(int fourCC) {
            InternalFormat format;
            switch (fourCC) {
                case FOURCC_DXT1:
                    format = InternalFormat.CompressedRgbaS3tcDxt1Ext;
                    break;
                case FOURCC_DXT3:
                    format = InternalFormat.CompressedRgbaS3tcDxt3Ext;
                    break;
                case FOURCC_DXT5:
                    format = InternalFormat.CompressedRgbaS3tcDxt5Ext;
                    break;
                default:
                    throw new Exception("unsupported compression format");
            }

            return format;
        }
    }
}
